package info.motteke.mixivoice4j;

import info.motteke.mixivoice4j.body.BodyElement;
import info.motteke.mixivoice4j.body.BodyVisitor;
import info.motteke.mixivoice4j.body.IconElement;
import info.motteke.mixivoice4j.body.LinkElement;
import info.motteke.mixivoice4j.body.StringElement;
import info.motteke.mixivoice4j.body.ToStringVisitor;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode;

/**
 * つぶやきの本文を表すクラスです。
 *
 * @author Y.Wakamatsu
 *
 */
public class Body {

    private static final Log log = LogFactory.getLog(Body.class);

    /** 本文を構成する要素を保持します。 */
    private final List<BodyElement> body;

    public Body(Element element) {
        List<BodyElement> body = parse(element);
        this.body = Collections.unmodifiableList(body);
    }

    /**
     *
     * @param element
     * @return
     */
    private List<BodyElement> parse(Element element) {
        List<BodyElement> list = new ArrayList<BodyElement>();

        for (Node node : element.childNodes()) {
            if (node instanceof TextNode) {
                list.add(parseText((TextNode) node));
                continue;
            }
            if (node instanceof Element) {
                Element el = (Element) node;
                String tagname = el.tagName();
                if (tagname.equals("a")) {
                    list.add(parseLink(el));
                    continue;
                }
                if (tagname.equals("img")) {
                    list.add(parseImage(el));
                    continue;
                }
                if (tagname.equals("span")) {
                    /* spanは無視 */
                    continue;
                }
                log.info("未知のタグ : " + tagname);
                continue;
            }
            log.info("未知のノード : " + node.getClass().getSimpleName());
        }
        return list;
    }

    private BodyElement parseText(TextNode node) {
        return new StringElement(node.getWholeText().trim().replaceAll("[\n\r]", ""));
    }

    private BodyElement parseLink(Element element) {
        return new LinkElement(element.attr("href"), element.text());
    }

    private BodyElement parseImage(Element element) {
        String src = element.attr("src");
        int id = Integer.parseInt(src.replaceAll(".*/([0-9]+)\\.gif", "$1"));
        return new IconElement(src, element.attr("alt"), id);
    }

    /**
     * {@linkplain BodyVisitor}を利用して本文を構築し、その値を返します。
     *
     * @param <T>
     *            返り値
     * @param visitor
     *            要素を処理するクラス
     * @return visitorによって再構築された本文
     */
    public <T> T getValue(BodyVisitor<T> visitor) {
        for (BodyElement elem : body) {
            elem.accept(visitor);
        }

        return visitor.getValue();
    }

    /**
     * 本文を文字列化します。
     */
    public String toString() {
        return getValue(new ToStringVisitor());
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((body == null) ? 0 : body.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Body other = (Body) obj;
        if (body == null) {
            if (other.body != null)
                return false;
        } else if (!body.equals(other.body))
            return false;
        return true;
    }
}
